#ifndef CE_GFX_MESH_PATCHMESH_H
#define CE_GFX_MESH_PATCHMESH_H

#include <math/boundingbox.h>
#include <gfx/ipatchmesh.h>
#include <vector>

struct iIndexBuffer;
struct iVertexBuffer;
struct iVertexDeclaration;

/**
 * \addtogroup opengl
 * @{
 */

/**
 * @brief Implementation of the iPatchMesh interface.
 */
class CE_API cePatchMeshGL20 : public iPatchMesh
{
  CE_OBJECT
public:
  enum PatchNeighbour
  {
    PN_NegP,
    PN_NegQ,
    PN_PosP,
    PN_PosQ,
    PN_Count
  };

  struct Patch
  {
    unsigned          offset;
    unsigned          dp;
    unsigned          dq;
    unsigned          stepP;
    unsigned          stepQ;
    unsigned          maxStepP;
    unsigned          maxStepQ;
    bool              dirty;

    iIndexBuffer*     indexBuffer;
    unsigned          assignedIndices;
    Patch*            neighbours[PN_Count];
    ceBoundingBox			bbox;
		float							maxDistance;
  };

private:
  ceBoundingBox					_bbox;
  unsigned              _numPatchP;
  unsigned              _numPatchQ;
  std::vector<Patch*>   _patches;

  unsigned              _numP;
  unsigned              _numQ;
  bool                  _wrapP;
  bool                  _wrapQ;
  iVertexBuffer*        _vertices;
  iVertexDeclaration*   _vertexDeclaration;
  float                 _maxDistance;

  bool                  _dirty;

  Patch* CreatePatch(iDevice* device, unsigned offset, unsigned dp, unsigned dq);
  void UpdatePatch (Patch* patch);
  unsigned GetNeighbourStepP (Patch* patch, PatchNeighbour neighbour);
  unsigned GetNeighbourStepQ (Patch* patch, PatchNeighbour neighbour);


  bool CreatePatches (iDevice* device, unsigned numP, unsigned numQ, bool wrapP = false, bool wrapQ = false);
  bool UpdatePatchBoundingBoxes (float maxDistance);
  void InitializePatches (unsigned stepSize = 1);

protected:

public:
  cePatchMeshGL20();
  ~cePatchMeshGL20 ();

  virtual bool Initialize (iDevice*					device,
													 iVertexBuffer			*vertices, 
													 unsigned						numVertexP, 
													 unsigned						numVertexQ,
													 iVertexDeclaration *vertexDeclaration,
													 unsigned						numPatchesP, 
													 unsigned						numPatchesQ, 
													 float							distance,
													 bool								wrapP = false, 
													 bool								wrapQ = false);


  const ceBoundingBox& GetBoundingBox () const;
  ceBoundingBox& GetBoundingBox ();


  virtual void Draw (const ceRenderEnvironment& renderEnvironment, iDevice* device);
  virtual void Update (const ceRenderEnvironment& renderEnvironment, iDevice* device);

  unsigned GetNumberOfPatches () const;
  Patch* GetPatch (unsigned);
  Patch* GetPatch (int p, int q);
  Patch* GetPatch (int p, int q, bool wrapP, bool wrapQ);

  bool CalcSmoothNormals ();

  void Copy (iDevice* device, iPatchMesh* mesh);
  virtual iPatchMesh* Clone (iDevice* device);
};

/** @} */

#endif // CE_GFX_MESH_PATCHMESH_H
